package com.tianji.tjpromotion.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.common.domain.R;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.*;
import com.tianji.tjpromotion.constants.PromotionConstants;
import com.tianji.tjpromotion.domain.dto.CouponFormDTO;
import com.tianji.tjpromotion.domain.dto.CouponIssueFormDTO;
import com.tianji.tjpromotion.domain.po.Coupon;
import com.tianji.tjpromotion.domain.po.CouponScope;
import com.tianji.tjpromotion.domain.po.UserCoupon;
import com.tianji.tjpromotion.domain.vo.CouponDetailVO;
import com.tianji.tjpromotion.domain.vo.CouponPageVO;
import com.tianji.tjpromotion.domain.vo.CouponScopeVO;
import com.tianji.tjpromotion.domain.vo.CouponVO;
import com.tianji.tjpromotion.enums.CouponStatus;
import com.tianji.tjpromotion.enums.ObtainType;
import com.tianji.tjpromotion.enums.UserCouponStatus;
import com.tianji.tjpromotion.mapper.CouponMapper;
import com.tianji.tjpromotion.query.CouponQuery;
import com.tianji.tjpromotion.service.ICouponScopeService;
import com.tianji.tjpromotion.service.ICouponService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.tjpromotion.service.IExchangeCodeService;
import com.tianji.tjpromotion.service.IUserCouponService;
import io.reactivex.internal.util.HalfSerializer;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.Temperature;
import org.redisson.misc.Hash;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 优惠券的规则信息 服务实现类
 * </p>
 *
 * @author yp
 * @since 2024-08-02
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements ICouponService {

    private final ICouponScopeService couponScopeService;


    private final IExchangeCodeService exchangeCodeService;
    private final StringRedisTemplate redisTemplate;
    private final IUserCouponService iUserCouponService;

    /**
     * 新增优惠卷
     *
     * @param dto
     */
    @Override
    @Transactional
    public void saveCoupon(CouponFormDTO dto) {
        //转换Coupon(一条记录)
        Coupon coupon = BeanUtils.copyBean(dto, Coupon.class);
        //2.保存Coupon(一条记录)
        save(coupon);
        //3.判断用户是否限定了范围
        Boolean specific = dto.getSpecific();
        if (!specific) {
            return;
        }
        //如果限定了范围
        List<Long> scopes = dto.getScopes();
        if (CollUtils.isEmpty(scopes)) {
            throw new BizIllegalException("业务不合法");
        }
        Long couponId = coupon.getId();
        List<CouponScope> collect = scopes.stream().map(CId -> new CouponScope().setBizId(CId).setCouponId(couponId))
                .collect(Collectors.toList());

        couponScopeService.saveBatch(collect);
    }


    /**
     * 分页查询优惠卷
     *
     * @param query
     * @return
     */
    @Override
    public PageDTO<CouponPageVO> queryPage(CouponQuery query) {
        //1.分页查询coupon
        String name = query.getName();
        Integer status = query.getStatus();
        Integer type = query.getType();
        Page<Coupon> page = lambdaQuery()
                .eq(status != null, Coupon::getStatus, status)
                .eq(type != null, Coupon::getDiscountType, type)
                .like(StringUtils.isNotEmpty(name), Coupon::getName, name)
                .page(query.toMpPageDefaultSortByCreateTimeDesc());
        List<Coupon> couponLists = page.getRecords();
        if (CollUtils.isEmpty(couponLists)) {
            return PageDTO.empty(page);
        }
        //2.返回CouponPageVo返回
        List<CouponPageVO> volist = BeanUtils.copyList(couponLists, CouponPageVO.class);


        return PageDTO.of(page, volist);
    }


    /**
     * 发送优惠卷
     *
     * @param dto
     */
    @Override
    public void issueCoupon(CouponIssueFormDTO dto) {
        log.info("issueCoupon--{}", Thread.currentThread().getId());
        //1.根据id查询id
        Coupon old = getById(dto.getId());
        if (old == null) {
            throw new DbException("优惠卷不存在");
        }
        //2判断数据库之前的状态
        if (old.getStatus() != CouponStatus.DRAFT && old.getStatus() != CouponStatus.PAUSE) {
            throw new BizIllegalException("优惠卷的状态有误不能发布");
        }
        //3.判断是否是立即发放(如果立即发放就会没有优惠卷领取的时间的)
        boolean isBegin = dto.getIssueBeginTime() == null;
        //4.更新优惠卷
        //4.1考培属性
        Coupon coupon = BeanUtils.copyBean(dto, Coupon.class);
        //4.2更新状态和发放时间
        if (isBegin) {
            coupon.setStatus(CouponStatus.ISSUING);
            coupon.setIssueBeginTime(LocalDateTime.now());
        } else {
            coupon.setStatus(CouponStatus.UN_ISSUE);
        }
        //4.3写入数据库
        updateById(coupon);

        //5.如果优惠卷是立刻发放，将优惠卷的信息（优惠卷id,领卷的开始时间，领卷的结束时间，发行总数量，限领数量）存储到redis里里面
        if (isBegin) {
            String key = PromotionConstants.COUPON_CACHE_KEY_PREFIX + dto.getId();
            HashMap<String, String> map = new HashMap<>();
            map.put("issueBeginTime", coupon.getIssueBeginTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            map.put("issueEndTime", coupon.getIssueEndTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            map.put("totalNum", String.valueOf(old.getTotalNum()));
            map.put("userLimit", String.valueOf(old.getUserLimit()));
            redisTemplate.opsForHash().putAll(key, map);
        }

        //5.判断领取的类型，如果是兑换码，生成兑换码
        if (old.getObtainWay() == ObtainType.ISSUE && old.getStatus() == CouponStatus.DRAFT) {
            old.setIssueEndTime(dto.getIssueEndTime());//old 新增优惠卷的时候没有优惠卷的领取时间 过期时间在
            exchangeCodeService.generateCode(old);
        }
    }


    /**
     * 修改优惠卷
     *
     * @param dto
     * @param id
     */
    @Override
    @Transactional
    public void updateCoupon(CouponFormDTO dto, Long id) {
        //1.判断优惠券推广方式，如果是兑换码则需要验证兑换码数量
        dto.setId(id);
        checkObtainWay(dto);
        Coupon old = getById(id);
        if (old == null) {
            throw new BadRequestException("优惠卷不存在");
        }

        Coupon coupon = BeanUtils.copyBean(dto, Coupon.class);
        coupon.setId(id);
        coupon.setStatus(null);
        updateById(coupon);
        if (dto.getSpecific() == null || (!old.getSpecific() && !dto.getSpecific())) {
            return;
        }
        if (dto.getSpecific() && CollUtils.isEmpty(dto.getScopes())) {
            return;
        }

        //2.删除原来的限定范围
        Long couponId = coupon.getId();
        couponScopeService.removeByCouponId(couponId);

        //3.新增新的限定范围
        List<Long> scopes = dto.getScopes();
        List<CouponScope> collect = scopes.stream().map(cid -> new CouponScope().setCouponId(couponId).setBizId(cid)).collect(Collectors.toList());
        couponScopeService.saveBatch(collect);

    }

    /**
     * 根据id进行删除优惠卷
     *
     * @param id
     */
    @Override
    @Transactional
    public void deleteCouponbyId(Long id) {
        Coupon coupon = getById(id);
        if (coupon.getStatus() != CouponStatus.DRAFT) {
            return;
        }
        removeById(coupon);
        //查询与之关联的限定范围
        List<CouponScope> lists = couponScopeService.selectBycoupondId(id);
        if (CollUtils.isNotEmpty(lists)) {
            couponScopeService.removeByCouponId(id);
        }
    }


    /**
     * 跟距优惠卷的id进行查询
     *
     * @param id
     */
    @Override
    @Transactional
    public CouponDetailVO queryById(Long id) {
        Coupon coupon = getById(id);
        if (coupon == null) {
            throw new DbException("优惠卷不存在");
        }
        CouponDetailVO vo = BeanUtils.copyBean(coupon, CouponDetailVO.class);

        if (coupon.getSpecific() == null) {
            return vo;
        }
        List<CouponScope> list = couponScopeService.selectBycoupondId(id);
        return vo;
    }


    /**
     * 用户端查询正在发放的优惠卷
     *
     * @return
     */
    @Override
    public List<CouponVO> queryIssueCouponList() {

        //1.查询【状态】是正在进行，【发放状态】是手动领取的
        List<Coupon> couponList = lambdaQuery()
                .eq(Coupon::getStatus, CouponStatus.ISSUING)
                .eq(Coupon::getObtainWay, ObtainType.PUBLIC)
                .list();
        if (CollUtils.isEmpty(couponList)) {
            return CollUtils.emptyList();
        }
        //2.根据优惠卷ids和用户id查询用户表，获取当前的用户领取的优惠卷
        Long userId = UserContext.getUser();
        //1.获取cids
        List<Long> cIds = couponList.stream().map(Coupon::getId).collect(Collectors.toList());


        //3.解决IssueNum 发放中 ， 还是正在使用领取
        List<UserCoupon> userCouponLists = iUserCouponService.lambdaQuery()
                .eq(UserCoupon::getUserId, userId)
                .in(UserCoupon::getCouponId, cIds)
                .list();
        Map<Long, Long> issuedMap = userCouponLists.stream().collect(Collectors.groupingBy(UserCoupon::getCouponId, Collectors.counting()));

        //4.List<UserCoupon> 转成Map
        Map<Long, Long> unusedMap = userCouponLists.stream().
                filter(uc -> uc.getStatus() == UserCouponStatus.UNUSED)
                .collect(Collectors.groupingBy(UserCoupon::getCouponId, Collectors.counting()));


        List<CouponVO> couponVOList = new ArrayList<>();
        for (Coupon coupon : couponList) {
            //每遍历一次就要封装成一个vo
            CouponVO vo = BeanUtils.copyBean(coupon, CouponVO.class);
            //5.1封装available
            vo.setAvailable(coupon.getIssueNum() < coupon.getTotalNum() && issuedMap.getOrDefault(coupon.getId(), 0L) < coupon.getUserLimit());
            //5.2封装received
            vo.setReceived(unusedMap.getOrDefault(coupon.getId(), 0L) > 0);
            couponVOList.add(vo);
        }
        return couponVOList;
    }

    private void checkObtainWay(CouponFormDTO dto) {
        ObtainType obtainWay = dto.getObtainWay();
        if (ObtainType.ISSUE.equals(obtainWay)) {
            if (dto.getTotalNum() < 1 || dto.getTotalNum() > 5000) {
                throw new BadRequestException("优惠卷数量不正确");
            }
        }

    }


}
